---
--类名: Npc.lua
--创建日期: 2015-5-4
--创建人: 陈小虎
--功能描述: Npc模型
--修改内容：
--修改日期:2018-5-4
--修改人:galileoliu
--修改备注:
--
local Vector = require("app.utils.Vector")

local AvatarFactory = require("app.public.avatar.AvatarFactory")

local Role = import(".Role")

local Npc = class("Npc",function()
    return Role.new()
end)

Npc.TYPE_ORE = 1--矿石
Npc.TYPE_FISH = 2--渔点
Npc.TYPE_TOAST = 3--烤肉架
Npc.TYPE_CHESTS = 4--宝箱
Npc.TYPE_HERB = 5--草药
Npc.TYPE_IDENTIFY = 7--放大镜
Npc.TYPE_WRECK = 8 --摧毁物
Npc.TYPE_GRABFISH = 9 -- 抓鱼

NpcMarkResPath =
{
    commonTalk = Res.Pop_Talk,
    prestigeDonate = Res.Pop_Shengwang,
    mining = Res.Pop_Mining,
    fishing = Res.Pop_Fishing,
    grabFish = Res.Pop_GrabFish,
    bossCall = Res.Pop_BossCall,
    prestigeShop = Res.Pop_Shop,
    jobcike = Res.Pop_Js_Cike,
    jobfashi = Res.Pop_Js_FaShi,
    joblieren = Res.Pop_Js_LieRen,
    jobmushi = Res.Pop_Js_MuShi,
    jobzhanshi = Res.Pop_Js_ZhanShi,
    compound = Res.Pop_Compound,
    recast = Res.Pop_Recast,
    identify = Res.Pop_Identify,
    herb = Res.Pop_Herb,
    chests = Res.Pop_Chests,
    transfer = Res.Pop_Transfer,
    arena = Res.Pop_Arena,
    exploration = Res.Pop_HangUp,
    disaster = Res.Pop_Disaster,
    patrol = Res.Pop_Patrol,
    monstercall = Res.Pop_MonsterCall,
    toycall = Res.Pop_MonsterCall,
    guild = Res.Pop_Guild,
    daily = Res.Pop_Daily,
    arena_season = Res.Pop_ArenaSeason,
    mainTask = Res.Pop_MainTask,
    sideTask = Res.Pop_SideTask,
    loopTask = Res.Pop_LoopTask,
    circleTask = Res.Pop_CicleTask,
    escortTask = Res.Pop_HusongTask,
    branchTask = Res.Pop_BranchTask,
    taskBattle = Res.Pop_TaskBattle,
    worldBossBattle = Res.Pop_WorldBossBattle,
    guildBattle = Res.Pop_GuildBattle,
    raid = Res.Pop_BossCall,
    partner = Res.Pop_Partner,
    gatherWater = Res.Pop_GatherWater,
    wreck = Res.Pop_Wreck,
    battle = Res.Pop_BossCall,
}

local NodeGetPositionFunc = cc.Node.getPosition
local NodeSetPositionFunc = cc.Node.setPosition
local NodeGetPositionXFunc = cc.Node.getPositionX
local NodeSetPositionXFunc = cc.Node.setPositionX
local NodeGetPositionYFunc = cc.Node.getPositionY
local NodeSetPositionYFunc = cc.Node.setPositionY
local NodeIsVisibleFunc = cc.Node.isVisible
local NodeSetVisibleFunc = cc.Node.setVisible
local NodeToWorldPosition = cc.Node.toWorldPosition
local NodeGetScaleFunc = cc.Node.getScale
local NodeSetScaleFunc = cc.Node.setScale
local NodeGetScaleXFunc = cc.Node.getScaleX
local NodeSetScaleXFunc = cc.Node.setScaleX
local NodeGetScaleYFunc = cc.Node.getScaleY
local NodeSetScaleYFunc = cc.Node.setScaleY

local MathFloor = math.floor

function Npc:ctor(id,async,displayId)
    cc(self):addComponent("components.behavior.EventProtocol"):exportMethods()
    self.mId = id
    self:setEventRange(0,0,0,0)
    self.mPlayEvents = false
    self.mEventsVec = Vector.new()

    self.mEscortScripts = {}
    self.mDungeonEvents = {}

    self.mWaitStyleSeconds = 0

    self.mOffsetY = 0

    self.mFaceTo = 0

    self._markCondEnough = true

    self.mArmParent = display.newNode()
    self.mArmParent:setCascadeColorEnabled(true)
    self.mArmParent:setCascadeOpacityEnabled(true)
    self:addChild(self.mArmParent)


    displayId = displayId or self:getId()
    self.mArmatrueNode = AvatarFactory.createNpcById(displayId)
    if self.mArmatrueNode == nil then
        self.mArmatrueNode = AvatarFactory.createNpcById(11051101)
    end
    self.mArmParent:addChild(self.mArmatrueNode)
    self.mOriginalScale = NodeGetScaleFunc(self.mArmatrueNode)
    self:scaleTo(self.m_scaleToValue)
    self.mAdjustPos = true
    self:playDefaultAction()

    self._scriptFlag = {}
    self._events = {}
end

function Npc:getRootArmature()
    return self.mArmatrueNode
end

function Npc:setTaskGathered(isGathered)
    self._taskGathered = isGathered
end

function Npc:isTaskGathered()
    return self._taskGathered
end

function Npc:setGatherType(isGatherType)
    self._gatherType = isGatherType
end

function Npc:isGatherType()
    return self._gatherType
end

--function Npc:setAuto(isAuto)
--    self._isAuto = isAuto
--end
--
--function Npc:isAuto()
--    return self._isAuto
--end

function Npc:setTaskId(taskId)
    self._taskId = taskId
end

function Npc:getTaskId()
    return self._taskId
end

--function Npc:setMapType(mapType)
--    self.m_mapType = mapType
--end

--function Npc:getMapType()
--    return self.m_mapType
--end

--function Npc:setMapShow(mapShow)
--    self.m_mapShow = mapShow
--end

--function Npc:getMapShow()
--    return self.m_mapShow
--end

--function Npc:setMapListShow(mapListShow)
--    self.m_mapListShow = mapListShow
--end
--
--function Npc:getMapListShow()
--    return self.m_mapListShow
--end

function Npc:setGathering(isGathering)
    self.m_gathering = isGathering
end

function Npc:isGathering()
    return self.m_gathering
end

function Npc:setChestsDisappear(disappear)
    self.m_chestsDisappear = disappear
end

function Npc:isChestsDisappear()
    return self.m_chestsDisappear
end

function Npc:excuteAction(action)
    self:runAction(action)
    if self.mHeadNameNode then
        self.mHeadNameNode:runAction(action:clone())
    end
    if self.mBubble then
        self.mBubble:runAction(action:clone())
    end
    if self.mCurrentMark then
        self.mCurrentMark:runAction(action:clone())
        -- self:setCurrentMark(nil)
    end
--    if self.mGuideMark then
--        self.mGuideMark:runAction(action:clone())
--    end
end

function Npc:excuteStopAllActions()
    self:stopAllActions()
    if self.mHeadNameNode then
        self.mHeadNameNode:stopAllActions()
    end
    if self.mBubble then
        self.mBubble:stopAllActions()
    end
    if self.mCurrentMark then
        self.mCurrentMark:stopAllActions()
    end
--    if self.mGuideMark then
--        self.mGuideMark:stopAllActions()
--    end
end

function Npc:excuteNameFadeOut()
    if self.mHeadNameNode then
        if not self.mHeadNameFadeOut then
            self.mHeadNameFadeOut = true
            local action = cc.Sequence:create(cc.FadeTo:create(0.2,0))
            self.mHeadNameNode:stopAllActions()
            self.mHeadNameNode:runAction(action)
        end
    end
end

function Npc:excuteNameFadeIn()
    if self.mHeadNameNode then
        if self.mHeadNameFadeOut then
            self.mHeadNameFadeOut = false
            local action = cc.Sequence:create(cc.FadeTo:create(0.2,255))
            self.mHeadNameNode:stopAllActions()
            self.mHeadNameNode:runAction(action)
        end
    end
end

function Npc:setNameNode(nameNode)
    assert(nameNode)
    assert(self.mHeadNameNode==nil)

    self.mHeadNameNode = nameNode

    if nameNode then
        self:setName("")
        self:setTitle("")
        self:addAttachment(nameNode,"point",0,0)
    end
end

function Npc:setWithLegion(isWithLegion)
    if self._withLegion ~= isWithLegion then
        self._withLegion = isWithLegion
    end
end

function Npc:isWithLegion()
    return self._withLegion
end

function Npc:forceNameNodeVisiblity(visible)
    if not self.mHeadNameNode then return end
    -- if self:isWithLegion() and not self:isNoSetMark() then
    --     if NodeIsVisibleFunc(self.mHeadNameNode) then
    --         NodeSetVisibleFunc(self.mHeadNameNode,false)
    --     end
    --     return
    -- end
    if self:isStopShowUpHead() and visible then
        return
    end
    if NodeIsVisibleFunc(self.mHeadNameNode) ~= visible then
        NodeSetVisibleFunc(self.mHeadNameNode,visible)
    end
end

function Npc:forceMarkVisibility(visible)
    if not self.mCurrentMark then return end
    -- if self:isWithLegion() and not self:isNoSetMark() then
    --     if NodeIsVisibleFunc(self.mCurrentMark) then
    --         NodeSetVisibleFunc(self.mCurrentMark,false)
    --     end
    --     return
    -- end
    if self:isStopShowUpHead() and visible then
        return
    end
    if NodeIsVisibleFunc(self.mCurrentMark) ~= visible then
        NodeSetVisibleFunc(self.mCurrentMark,visible)
    end
end

function Npc:allVisibility(visible,bodyOnly)
    if not visible then
        self:hideBubble()
        self:setDoingEvents(false)
    end
    if visible ~= NodeIsVisibleFunc(self) then
        if not bodyOnly then
            self:forceNameNodeVisiblity(visible)
            self:currentMarkVisibility(visible)
        end

        NodeSetVisibleFunc(self,visible)
    end
end

--function Npc:allMarkVisibility(visible)
--    self:forceMarkVisibility(visible)
--    self:forceStopMarkVisible(not visible)
--end

function Npc:isNoSetMark()
    local mark = self:getMark()
    if mark == "disaster" then
        if (self:getType() >= 6 and self:getType() <= 12)
            or (self:getType() >= 20 and self:getType() <= 24) then
            return true
        end
    else
        return mark == "transfer"
            or mark == "raid"
            or mark == "prestigeShop"
            or mark == "toycall"
    end
    return false
end

function Npc:allNameNodeVisibility(visible)
    self:forceNameNodeVisiblity(visible)
end

function Npc:stopShowUpHead(isStop)
    if self.mStopShowUpHead ~= isStop then
        self.mStopShowUpHead = isStop
    end
end

function Npc:isStopShowUpHead()
    return self.mStopShowUpHead
end

function Npc:removeAll()
    if self.mHeadNameNode then
        self.mHeadNameNode:removeFromParent()
        self.mHeadNameNode = nil
    end
    if self.mCurrentMark then
        self:setCurrentMark(nil)
    end
    if self.mBubble then
        self.mBubble:removeFromParent()
        self.mBubble = nil
    end
end

function Npc:setSingleParams(params)
    self._singleParams = params
end

function Npc:getSingleParams()
    return self._singleParams
end

--function Npc:setEscorting(escorting)
--    self.mEscorting = escorting
--end

--function Npc:isEscorting()
--    return self.mEscorting
--end

function Npc:setOwner(isOwner)
    self.mIsOwner = isOwner
end

function Npc:isOwner()
    return self.mIsOwner
end

function Npc:setUiHideType(uiHideType)
    self.mUiHideType = uiHideType
end

function Npc:getUiHideType()
    return self.mUiHideType
end

function Npc:setOriginalX(x)
    self.mOriginalX = x
end

function Npc:getOriginalX()
    return self.mOriginalX
end

function Npc:setOriginalDirection(oDir)
    self.mOriginalDirection = oDir
end

function Npc:getOriginalDirection()
    return self.mOriginalDirection
end

---
-- 设置是否面向主角 faceTo 0 不转向 1 转向
--
function Npc:setFaceTo(faceTo)
    self.mFaceTo = faceTo
end

function Npc:getFaceTo()
    return self.mFaceTo
end

function Npc:isFaceTo()
    if self:getFaceTo() == 0 then
        return false
    else
        return true
    end
end

function Npc:setDefaultAction(defaultAction)
    if not defaultAction then
        self.mDefaultAction = Role.ACTION_MAP_IDLE
    else
        self.mDefaultAction = defaultAction
    end
end

function Npc:getDefaultAction()
    return self.mDefaultAction
end

function Npc:playDefaultAction()
    self:playAction(self:getDefaultAction())
end

function Npc:setStyleAction(styleAction)
    local armature = self.mArmatrueNode
    if armature and armature:hasAnimation(styleAction) then
        self.mStyleAction = styleAction
    end
end

function Npc:getStyleAction()
    return self.mStyleAction
end

function Npc:setStyleMinSeconds(minSeconds)
    self.mMinStyleSeconds = minSeconds
end

function Npc:getStyleMinSeconds()
    return self.mMinStyleSeconds
end

function Npc:setStyleMaxSeconds(maxSeconds)
    self.mMaxStyleSeconds = maxSeconds
end

function Npc:getStyleMaxSeconds()
    return self.mMaxStyleSeconds
end

function Npc:countStyleSeconds()
    self.mStyleSeconds = math.random(self:getStyleMinSeconds(),self:getStyleMaxSeconds())
end

function Npc:updatePlayStyleAction(dt)
    local styleAtion = self.mStyleAction
    if not styleAtion then
        return
    end

    if StoryManager:isEnterStoryMode() then
        return
    end

    if self.mPlayStyleAction then
        return
    end

    self.mWaitStyleSeconds = self.mWaitStyleSeconds + dt
    self.mStyleSeconds = self.mStyleSeconds or math.random(self:getStyleMinSeconds(),self:getStyleMaxSeconds())

    if self.mWaitStyleSeconds >= self.mStyleSeconds then

        self.mWaitStyleSeconds = 0
        self.mStyleSeconds = nil

        self:stopMove()

        self.mPlayStyleAction = true

        local function callback()
            self.mPlayStyleAction = false
            self:standBy()
        end

        self:setCurAction(styleAtion)
        self.mArmatrueNode:play(styleAtion, 1, callback)
    end
end

function Npc:stopAutoMove(stop)
    self.mStopAutoMove = stop
end

function Npc:isStopAutoMove()
    return self.mStopAutoMove
end


function Npc:move(cx,blx,brx)
    self:forcePlayAction(Role.ACTION_MAP_MOVE)

    if self.mDirection == Role.DIR_LEFT then
        if cx > blx then
            self:setTargetX(blx)
        else
            self:setTargetX(brx)
        end
    elseif self.mDirection == Role.DIR_RIGHT then
        if cx < brx then
            self:setTargetX(brx)
        else
            self:setTargetX(blx)
        end
    end
end

---
-- cc.c3b(0,0,0)
--
function Npc:setNameColor(color)
    local headNameNode = self.mHeadNameNode
    if headNameNode then
        headNameNode:setNameColor(color)
        headNameNode:setTitleColor(color)
    end
end

function Npc:setSoundPath(soundPath)
    if soundPath ~= nil then
        local sIndex, eIndex = string.find(soundPath,".mp3")
        if sIndex == nil then
            sIndex, eIndex = string.find(soundPath,".wav")
        end
        if sIndex ~= nil then
            self.mSoundPath = "sfx/" .. soundPath
            -- print("Npc:setSoundPath  self.mSoundPath = ",self.mSoundPath)
            -- self.mAudioId = AM.play(self.mSoundPath,true)
            -- AM.setVolume(self.mAudioId,0)
        else
            self.mSoundPath = nil
            self.mAudioId = nil
        end

    else
        self.mSoundPath = nil
        self.mAudioId = nil
    end

end

function Npc:getSoundPath()
    return self.mSoundPath
end

function Npc:adjustSounds(cx)
    if self:getSoundPath() then
        local ox = NodeGetPositionXFunc(self)
        local musicVolume = 1.0
        if cx >= ox - self:getTalkRadius() and cx <= ox + self:getTalkRadius() then
            local volume = 1.0 - math.abs(cx - ox) / self:getTalkRadius()
            if not self.mAudioId then
                self.mAudioId = AM.play(self:getSoundPath(),true)
            end
            if self.mAudioId then
                AM.setVolume(self.mAudioId,volume)
            end
            musicVolume = 1.0 - volume
        else
            -- AM.setVolume(self.mAudioId,0)
            if self.mAudioId then
                AM.stop(self.mAudioId)
                self.mAudioId = nil
            end
        end
    end
end

-- function Npc:setTalkRadius(talkRadius)
--     if talkRadius then
--         self.mTalkRadius = tonumber(talkRadius)
--     else
--         self.mTalkRadius = 100
--     end

-- end

-- function Npc:getTalkRange()
--     local nx = self.mOriginalX
--     local radius = self.mTalkRadius
--     local lx = nx - radius - 5
--     local rx = nx + radius + 5
--     return lx,rx,nx
-- end

function Npc:isTargetInTalkRange(tatPos)
    local er = self:getEventRange()
    if er and tatPos >= er.tlx and tatPos <= er.trx then
        return true
    end
    return false
end

function Npc:getTalkRadius()
    return self.mTalkRadius
end

function Npc:setType(type)
    self.mType = type
end

function Npc:getType()
    return self.mType or -1
end

function Npc:setBattleOrder(order)
    self._battleOrder = order
end

function Npc:getBattleOrder()
    return self._battleOrder
end

---
-- 是否矿石点
--
function Npc:isOre()
    return self:getType() == Npc.TYPE_ORE
end

---
-- 是否渔点
--
function Npc:isFishPoint()
    return self:getType() == Npc.TYPE_FISH
end

---
--是否烤肉架
--
function Npc:isRoastPoint()
    return self:getType() == Npc.TYPE_TOAST
end

---
-- 是否宝箱
--
function Npc:isChests()
    return self:getType() == Npc.TYPE_CHESTS
end

---
-- 是否草药
--
function Npc:isHerb()
    return self:getType() == Npc.TYPE_HERB
end

---
-- 是否放大镜
--
function Npc:isIdentify()
    return self:getType() == Npc.TYPE_IDENTIFY
end

---
-- 是否摧毁物
--
function Npc:isWreck()
    return self:getType() == Npc.TYPE_WRECK
end

---
-- 是否抓鱼
--
function Npc:isGrabFish()
    return self:getType() == Npc.TYPE.GRABFISH
end

function Npc:touchMask()
    self.mTouchMask = true
    local function call()
        self.mTouchMask = false
    end

    local action = cc.Sequence:create(cc.DelayTime:create(2),cc.CallFunc:create(call))
    self:runAction(action)
end

function Npc:isTouchMask()
    return self.mTouchMask
end

function Npc:standBy()
    if self:getCurAction() ~= self:getDefaultAction() then
        self:playAction(self:getDefaultAction())
    end
end



function Npc:playAction(action)
    if self.mPlayEventAction then return end
    if self:getCurAction() == action then return end
    self.mArmatrueNode:stop()
    local play = self.mArmatrueNode:play(action,-1)
    if play then
        self:setCurAction(action)
    end
end

function Npc:forcePlayAction(action)
    self.mPlayEventAction = false
    self:playAction(action)
end

function Npc:setCurAction(action)
    self.mCurAction = action
end

function Npc:getCurAction()
    return self.mCurAction
end

function Npc:playActivityAction(aConfig)
    if not aConfig then return end
    if aConfig.action and aConfig.action.name then
        self._playActivityAction = true
        self:playEventAction(aConfig.action.name,nil,0,aConfig.action.loop)
    end
end

function Npc:stopActivityAction()
    if self._playActivityAction then
        self:stopEventAction()
        self._playActivityAction = false
    end
end

function Npc:playEventAction(action,callback,delayTime,isLoop,tPlayTimes,overKeep)
    local function compCallback()
        -- self:runAction(cc.Sequence:create(
        --     cc.DelayTime:create(0.05),
        --     cc.CallFunc:create(function()
        -- end)))
        self:stopEventAction()
    end

    local armatureNode = self.mArmatrueNode
    if not armatureNode then
        compCallback()
        return
    end
    armatureNode:stop()
    self.mPlayEventActionCallback = callback
    self.mPlayEventAction = true
    self.mPlayEventActionOverKeep = overKeep
    -- self:setOpacity(0)
    delayTime = delayTime or 0

    local playTimes = -1
    if not isLoop then
        playTimes = 1
    end
    playTimes = tPlayTimes or playTimes

    local playCount = 0

    --[[
    local function loopCallback()
        if isLoop then
            return
        end
        if playTimes == -1 then
            return
        end
        playCount = playCount + 1
        if playCount < playTimes then
            return
        end
        if not self:getEventMove() then
            compCallback()
        end
    end
    ]]

    if armatureNode:play(action,playTimes,compCallback) then
        self:setCurAction(action)
    else
        compCallback()
    end
end

function Npc:stopEventAction()
    self.mPlayEventAction = false
    if not self.mPlayEventActionOverKeep then
        self:standBy()
    end
    if self.mPlayEventActionCallback then
        self.mPlayEventActionCallback()
    end
end

-- 设置一次落脚点，调整Y坐标
function Npc:adjustPos(adjust)
    self.mAdjustPos = adjust
end

function Npc:setTargetX(tarx)
    if not tarx then return end
    self.mTargetX = MathFloor(tarx)
end

function Npc:getTargetX()
    return self.mTargetX
end

function Npc:autoMoveAction()
    if self:getCurAction() ~= Role.ACTION_MAP_MOVE then
        self:playAction(Role.ACTION_MAP_MOVE)
    end
end

function Npc:stopMove()
    self:setTargetX(NodeGetPositionXFunc(self))
end

function Npc:setEventTargetX(tarx,callback,lostDistance,lostFocusCall)
    self:setEventMove(true)
    -- self.mTargetX = tarx
    self:setTargetX(tarx)
    self:stopRayCast(false)
    self.mArriveCallback = callback

    self.mEventMoveStartX = NodeGetPositionXFunc(self)
    self.mLostFocusDistance = lostDistance
    self.mLostFocusCall = lostFocusCall
end

function Npc:noticeEventLostFocus()
    if self.mEventMove and self.mLostFocusDistance then
        local moveDis = math.abs(NodeGetPositionXFunc(self) - self.mEventMoveStartX)
        if moveDis >= self.mLostFocusDistance then
            if self.mLostFocusCall then
                self.mLostFocusCall()
                self.mLostFocusDistance = nil
            end
        end
    end
end

function Npc:setEventMove(move)
    self.mEventMove = move
end

function Npc:getEventMove()
    return self.mEventMove
end


function Npc:stopRayCast(stopRay)
    self.mStopRayCast = stopRay
end

function Npc:rayCastFoothold(newX)
    if self.mStopRayCast then return end
    if not newX then return end

    local standY = MapManager:getStandPointY(self:getLine(), MathFloor(newX))

    self:showLocation(newX, standY + self:getOffsetY())
    self:updateEventRange()
end

function Npc:setZValue(z)
    self.mZOrder = z
end

function Npc:getZValue(z)
    return self.mZOrder
end

---
--@param x
--
-- TODO 是否需要设置targetX ？？？(经测试，这里好像不能setTargetX)
function Npc:showLocationX(x)
    self:showLocation(x, display.height)
    self:rayCastFoothold(x)
end

function Npc:setOffsetY(offsetY)
    self.mOffsetY = offsetY
end

function Npc:getOffsetY()
    return self.mOffsetY
end

function Npc:setPlayEvents(play)
    self.mPlayEvents = play
end

function Npc:isPlayEvents()
    return self.mPlayEvents
end

function Npc:setBubbleEndCallback(callback)
    self._bubbleEndCallback = callback
end

function Npc:callBubbleEnd()
    if self._bubbleEndCallback then
        self._bubbleEndCallback()
    end
end

function Npc:setBubble(bubble)
    if self.mBubble then
        self.mBubble:removeFromParent()
        self.mBubble = nil
    end
    self.mBubble = bubble

    self.mBubble:setTouchEndCallback(function()
        self:callBubbleEnd()
    end)

    self:hideBubble()
end

function Npc:setOriginalScale(v)
    self.mOriginalScale = NodeGetScaleFunc(self.mArmatrueNode) * v
end

function Npc:getOriginalScale()
    return self.mOriginalScale
end

function Npc:scaleTo(scale)
    if not scale then return end
    self.m_scaleToValue = scale
    NodeSetScaleFunc(self.mArmatrueNode,scale * self:getOriginalScale())
end

function Npc:getSelfScale()
    return NodeGetScaleYFunc(self.mArmatrueNode)
end

function Npc:showBubble(content,showTime,overCall)
    local function callback()
        if not tolua.isnull(self) then
            self:hideBubble()
            self:standBy()
        end
        if overCall then
            overCall()
        end
    end

    if cc.isDead(self.mBubble) then
        self.mBubble = NodeFactory:createBubble()
        self.mBubble:setTouchEndCallback(function()
            self:callBubbleEnd()
        end)
    end

--    if self.mBubble then
        self.mBubble:stopAllActions()

        self.mBubble:setContent(content)

        if StoryManager:isEnterStoryMode() then
            self.mBubble:getParent():reorderChild(self.mBubble,SceneZOrder.storyModeUIOperateZOrder)
        else
            self.mBubble:getParent():reorderChild(self.mBubble,SceneZOrder.systemUIZOrder - 1)
        end
        self.mBubble:showLocation(self:getPointWorldPos())
        self:transitionBubble()
        self.mBubble:runAnimation()
        if showTime then
            self.mBubble:runAction(cc.Sequence:create(cc.DelayTime:create(tonumber(showTime)),cc.CallFunc:create(callback)))
        end

        if DEBUG == -1 then
            self.mBubble:setVisible(false)
        end
--    else
--        callback()
--    end
end

function Npc:hideBubble()
    if not cc.isDead(self.mBubble) then
        self.mBubble:stopAllActions()
        self.mBubble:hide()
        self.mBubble:removeFromParent()
        self.mBubble = nil
    end
end


function Npc:updateBubblePosition()
    if self.mBubble and NodeIsVisibleFunc(self.mBubble) then
        local convertPosX,convertPosY = self:getPointWorldPos()
        if StoryManager:isEnterStoryMode() then
            local bWidth = self.mBubble:getWidth()
            local bHeight = self.mBubble:getHeight()

            local maxH = convertPosY + bHeight
            if maxH > display.height then
                convertPosY = convertPosY - (maxH - display.height) - 10
            end

            if convertPosX > display.width / 2 then
                local maxX = convertPosX + bWidth / 2
                if maxX > display.width then
                    convertPosX = display.width - bWidth / 2 - 10
                end
            else
                local minX = convertPosX - bWidth / 2
                if minX < 0 then
                    convertPosX = bWidth / 2 + 10
                end
            end
        end

        self.mBubble:showLocation(convertPosX,convertPosY)
    end
end

function Npc:transitionBubble()
    if self.mBubble then
        local flipX = NodeGetScaleXFunc(self.mArmatrueNode)
        if flipX > 0 then
            self.mBubble:faceToRight()
        else
            self.mBubble:faceToLeft()
        end
    end

end

function Npc:faceToLeft()
    if self:isOre() or self:isFishPoint() then return end
    local scale = NodeGetScaleXFunc(self.mArmatrueNode)
    NodeSetScaleXFunc(self.mArmatrueNode,math.abs(scale))
    self:transitionBubble()
    self:setDirection(Role.DIR_LEFT)
end

function Npc:faceToRight()
    if self:isOre() or self:isFishPoint() then return end
    local scale = NodeGetScaleXFunc(self.mArmatrueNode)
    NodeSetScaleXFunc(self.mArmatrueNode,math.abs(scale)*-1)
    self:transitionBubble()
    self:setDirection(Role.DIR_RIGHT)
end

function Npc:faceToCharacter(character)
    if not character then
        return
    end
    if NodeGetPositionXFunc(character) < NodeGetPositionXFunc(self) then
        self:faceToLeft()
    else
        self:faceToRight()
    end
end

function Npc:setDoingEvents(isDoing)
    self.mDoingEvents = isDoing
end

function Npc:isDoingEvents()
    return self.mDoingEvents
end

function Npc:setMarkType(mType)
    self._markType = mType
end

function Npc:getMarkType()
    return self._markType
end

function Npc:resetMarkFromEvent(isDungeon)
    local over = false
    if self:getTaskMark() then
        over = true
        self:setMarkType("task")
        self:setMark(self:getTaskMark())
    elseif self:getGatherMark() then
        over = true
        self:setMarkType("gather")
        self:setMark(self:getGatherMark())
    elseif self:getCommonMark() then
        over = true
        self:setMarkType("common")
        self:setMark(self:getCommonMark())
    else
        local eventsVec = self:getEventsByFlag("talk")
        if not eventsVec then
            eventsVec = self:getEventsFrom(1)
        end
        if eventsVec then
            for i = 1,eventsVec:size() do
                local synEventsVec = eventsVec:get(i)
                if synEventsVec then
                    for j = 1,synEventsVec:size() do
                        local event = synEventsVec:get(j)
                        if event:getEventName() == event.SIGN_MARK_EVENT then
                            over = true
                            self:setMarkType(nil)
                            self:setMark(event.mMark)
                            break
                        end
                    end
                end
                if over then
                    break
                end
            end
        end
    end

    if not over then
        self:setMark(nil)
        self:removeMarkNode()
    end
end

function Npc:setMarkCondEnough(isEnough)
    self._markCondEnough = isEnough
end

function Npc:isMarkCondEnough()
    return self._markCondEnough
end

function Npc:addEvents(eventsVec)
    self.mEventsVec:add(eventsVec)
    self:resetMarkFromEvent()
end

function Npc:addEventsAt(index,eventsVec)
    self.mEventsVec:setElementAt(eventsVec,index)
    self:resetMarkFromEvent()
end

function Npc:getEventsFrom(index)
    if self.mEventsVec:size() >= 1 then
        return self.mEventsVec:get(index)
    end
    return nil
end

function Npc:removeEventsFirstElement()
    if self.mEventsVec:size() >= 1 then
        self.mEventsVec:removeAt(1)
    end
    self:resetMarkFromEvent()
end

function Npc:clearEvents()
    self.mEventsVec:clear()
    self:clearDungeonEvents()
    self:setMark(nil)
end

function Npc:haveEvents()
    if self.mEventsVec:size() >= 1 then
        return true
    end

    return false
end

function Npc:setVariety(variety)
    self._variety = variety
end

function Npc:isDungeon()
    return self._variety == Enums.NpcVariety.dungeon
end

function Npc:isFirstDungeonSwitch()
    return self.mDungeonEvents and ((self.mDungeonEvents.switch == 1) and true or false) or false
end

function Npc:addDungeonEvents(events,isFirst)
    if isFirst then
        self.mDungeonEvents["firtTimeEvents"] = events
    else
        self.mDungeonEvents["otherTimeEvents"] = events
    end
end

function Npc:getDungeonEvents()
    if self.mDungeonEvents.switch == 1 then
        return self.mDungeonEvents["firtTimeEvents"]
    else
        return self.mDungeonEvents["otherTimeEvents"]
    end
end

function Npc:setDungeonNpcSwitch(isFirst)
    if self.mDungeonEvents then
        self.mDungeonEvents.switch = isFirst and 1 or 0
    end
    self:resetMarkFromEvent(true)
end

function Npc:clearDungeonEvents()
    self.mDungeonEvents = {}
end

function Npc:addEscortEvents(eScripts)
    if not eScripts then return end
    local scripts = self:getEscortEventsByPosx(eScripts.posx)
    if not scripts then
        table.insert(self.mEscortScripts,eScripts)
    end
end

function Npc:getEscortEventsByPosx(posx)
    for i,v in ipairs(self.mEscortScripts) do
        local px = v.posx
        if px == posx then
            return v.events
        end
    end
    return nil
end

function Npc:clearEscortEvents()
    self.mEscortScripts = {}
end

--function Npc:updatePlayEsccortEvents()
--    if not self:isEscorting() then return end
--
--    for i,v in ipairs(self.mEscortScripts) do
--        local play = v.play
--        if not play then
--            local npcx = NodeGetPositionXFunc(self)
--            if (self.mDirection == Role.DIR_LEFT and v.triggerDir == "left" and npcx <= v.posx)
--                or (self.mDirection == Role.DIR_RIGHT and v.triggerDir == "right" and npcx >= v.posx) then
--                v.play = true
--                app:dispatchCustomEvent("EVENT_START_EVENTS",{events = clone(v.events),npcId = self:getId()})
--                break
--            end
--        end
--    end
--end

function Npc:setConfig(config)
    self._config = config
end

function Npc:getConfig()
    return self._config
end

function Npc:setScriptFlag(sType,flag)
    if not sType or not flag then return end
    self._scriptFlag[sType] = flag
end

function Npc:getScriptFlag(sType)
    return self._scriptFlag[sType]
end

function Npc:setEvents(sType,events)
    if not sType or not events then return end
    self._events[sType] = events
    self:resetMarkFromEvent()
end

-- function Npc:getEvents()
--     if self._events["task"] and self._events["task"]:size() > 0 then
--         return self._events["task"]
--     elseif self._events["talk"] and self._events["talk"]:size() > 0 then
--         return self._events["talk"]
--     end
--     return nil
-- end

function Npc:getEventsByFlag(flag)
    return self._events[flag]
end

function Npc:getBoundingBox()
    return self.mArmatrueNode:getBoundingBox()
end

function Npc:setOwnerGuildName(title)
    local headNameNode = self.mHeadNameNode
    if headNameNode then
        headNameNode:setOwnerGuildName(title)
    end
end

function Npc:setTitleText(text)
    self.mTitleText = text
end

function Npc:getTitleText()
    return self.mTitleText or ""
end

function Npc:setTitle(title)
    if not title then
        return
    end
    if not self:isShowName() then
        title = ""
    end
    self.mTitle = title
    if string.len(title) > 0 then
        self.mTitle = "<" .. title .. ">"
    end

    local headNameNode = self.mHeadNameNode
    if headNameNode then
        headNameNode:setTitle(title)
    end
end

function Npc:setShowName(isShow)
    self._showName = isShow
end

function Npc:isShowName()
    return self._showName
end

function Npc:setNameText(text)
    self.mNameText = text
end

function Npc:getNameText()
    return self.mNameText or ""
end

function Npc:getName()
    return self.mName
end

function Npc:setName(name)
    if not name then
        return
    end
    if not self:isShowName() then
        name = ""
    end
    self.mName = name

    self.mHeadNameNode:setName(name)
end

function Npc:updateNameLabelVisibility(visible)
    if self.mLabelName then
        if NodeIsVisibleFunc(self.mLabelName) ~= visible then
            NodeSetVisibleFunc(self.mLabelName,visible)
        end
    end
end

function Npc:forceDirection()
    if self.mDirection == Role.DIR_LEFT then
        self:faceToLeft()
    elseif self.mDirection == Role.DIR_RIGHT then
        self:faceToRight()
    end
end

function Npc:updateSecret(isLeader,isWithTeam)
    -- if NpcManager:isHideSecretNpcs(isLeader,isWithTeam) then
    --     self:hideSelf()
    --     self:stopShowUpHead(true)
    -- elseif MapManager:isSecretMap() then
    --     self:showSelf()
    --     self:stopShowUpHead(true)
    -- end
end

function Npc:setEventRange(lx ,rx ,nx,radius,talkRadius,tlx,trx)
    if not self.mEventRange then
        self.mEventRange = {lx = lx,rx = rx,nx = nx,radius = radius,talkRadius = talkRadius}
    else
        self.mEventRange.lx = lx
        self.mEventRange.rx = rx
        self.mEventRange.nx = nx
        self.mEventRange.radius = radius
        self.mEventRange.talkRadius = talkRadius
        self.mEventRange.tlx = tlx
        self.mEventRange.trx = trx
    end
end

function Npc:updateEventRange()
    local cx = self:getPositionX()
    local radius = self.mEventRange.radius or 0
    local tRadius = self.mEventRange.talkRadius or 0
    self.mEventRange.lx = cx - radius
    self.mEventRange.rx = cx + radius
    self.mEventRange.tlx = cx - tRadius
    self.mEventRange.trx = cx + tRadius
    self.mEventRange.nx = cx
end

function Npc:getEventRange()
    return self.mEventRange
end

function Npc:forceStopMarkVisible(visible)
    self.mIsForceStopVisible = visible
end

function Npc:isForceStopMarkVisible()
    return self.mIsForceStopVisible
end

function Npc:stopMarkForEvent(stop)
    self.mStopMark = stop
end

function Npc:isStopMarkForEvent()
    return self.mStopMark
end

--function Npc:setMarkVisible(mark,visible)
--    if self:isStopMarkForEvent() then
--        return
--    end
--
--    if not mark then
--        mark = self:getMark()
--    end
--
----    print("Npc:setMarkVisible ----- mark = ",mark)
----    print("Npc:setMarkVisible ----- visible = ",visible)
--
--    if not self:isTaskMark(mark) then
--        self:doSetMarkVisible(visible,mark)
--    end
--end

--function Npc:setTaskMarkVisible(mark,visible)
--    if not visible then
--        mark = self:getMark()
--    end
--
--    if self:isTaskMark(mark) then
--        self:doSetMarkVisible(visible,mark)
--    end
--end

function Npc:isTaskMark(mark)
    if mark == "mainTask"
        or mark == "sideTask"
        or mark == "loopTask"
        or mark == "circleTask"
        or mark == "escortTask" then
        return true
    end
    return false
end

---11023102 11023103
--@param mcx 主角当前坐标
--
function Npc:updateHeadSignMark(mcx)
    if self.mStopMark then
        return
    end

    if self.mIsForceStopVisible then
        return
    end

    if self.mDoingEvents then
        return
    end

    local mark = self:getMark()

--    self:setTaskMarkVisible(mark,true)

    -- print("Npc:updateHeadSignMark   self:getId() = ",self:getId())
    -- print("Npc:updateHeadSignMark   mark = ",mark)

    local eventRange = self:getEventRange()
    if not eventRange then
        return
    end

    local lx,rx,nx = eventRange.lx,eventRange.rx,eventRange.nx
    -- print("Npc:updateHeadSignMark   lx = ",lx)
    -- print("Npc:updateHeadSignMark   rx = ",rx)
    if mcx >= lx and mcx <= rx then
        self:currentMarkVisibility(true)
    else
        self:currentMarkVisibility(false)
    end

    if mcx >= nx - 50 and mcx <= nx + 50 then
        self:excuteNameFadeOut()
    else
        self:excuteNameFadeIn()
    end

end

--[[
    常规对话    commonTalk
    主线任务    mainTask
    支线任务    sideTask
    环任务接取  loopTask
    环任务     circleTask
    护送任务接取    escortTask
    声望捐献    prestigeDonate
    采矿       mining
    钓鱼       fishing
    BOSS召唤   bossCall
    声望商店    prestigeShop
    刺客标记    jobcike
    法师标记    jobfashi
    猎人标记    joblieren
    牧师标记    jobmushi
    战士标记    jobzhanshi
    合成标记    compound
    重铸标记    recast
    鉴定标记    identify
    草药标记    herb
    宝箱标记    chests
    传送标记    transfer
    竞技场标记  arena
    挂机       exploration
    天灾入侵    disaster
    巡逻       patrol
    怪物召唤    monstercall
    公会       guild
    每日任务    daily
    竞技场赛季  arena_season
--]]

function Npc:setMark(mark)
    if mark ~= self.mMark then
        self.mMark = mark
        -- self:dispatchEvent({name = "npc_mark_changed",npcId = self:getId()})
    end
end

function Npc:getMark()
    return self.mMark
end

function Npc:setTaskMark(mark)
    self._taskMark = (mark ~= nil) and string.trim(mark) or mark
end

function Npc:getTaskMark()
    return self._taskMark
end

function Npc:setGatherMark(gType)
    if gType == 1 then
        self._gatherMark = "mining"
    elseif gType == 2 then
        self._gatherMark = "fishing"
    elseif gType == 4 then
        self._gatherMark = "chests"
    elseif gType == 5 then
        self._gatherMark = "herb"
    elseif gType == 6 then
        self._gatherMark = "gatherWater"
    elseif gType == 7 then
        self._gatherMark = "identify"
    elseif gType == 8 then
        self._gatherMark = "wreck"
    elseif gType == 9 then
        self._gatherMark = "grabFish"
    else
        self._gatherMark = nil
    end
end

function Npc:getGatherMark()
    return self._gatherMark
end

function Npc:setCommonMark(mark)
    self._commonMark = (mark ~= nil) and string.trim(mark) or mark
end

function Npc:getCommonMark()
    return self._commonMark
end

function Npc:isToy()
    return self:getMark() == "toycall"
end

function Npc:getPointWorldPos()
    return self.mArmatrueNode:getBoneWorldPosition("point")
end

function Npc:setCurrentMark(markNode)
    self:removeMarkNode()

    self.mCurrentMark = markNode

--    if markNode == nil then
--        self.mMarkFadeIn = false
--    end

    if markNode then
        self:addAttachment(markNode,"point",0,0)
    end
end

function Npc:getCurrentMark()
    return self.mCurrentMark
end

function Npc:currentMarkVisibility(visible)
--    print("Npc:currentMarkVisibility()", self:getId(), self:getUid(), self:getMark(), self.mCurrentMark, visible)
    if not cc.isDead(self.mCurrentMark) then
        self.mCurrentMark:setVisible(visible)
    else
        local mark = self:getMark()
        local result = self:createMark(mark)
        if result then
            self.mCurrentMark:setVisible(visible)
        end
    end
end

function Npc:currentMarkFadeIn()
    if self.mMarkFadeIn then
        return
    end

    local mark = self.mCurrentMark
    if mark then
        self.mMarkFadeIn = true
        mark:setOpacity(0)
        local action = cc.Sequence:create(cc.FadeIn:create(0.5))
        mark:runAction(action)
    end
end

function Npc:currentMarkFadeOut()
    if not self.mMarkFadeIn then
        return
    end

    local mark = self.mCurrentMark
    if mark then
        self.mMarkFadeIn = false
--        local action = cc.Sequence:create(cc.FadeOut:create(0.5),cc.CallFunc:create(function()
--            self:removeMarkNode()
--        end))
        mark:stopAllActions()
        mark:runAction(cc.FadeOut:create(0.5))
    end
end

function Npc:playMarkAnimation(path)
    self:stopMarkAnimation()
    if self.mCurrentMark then
        self.mMarkAction = cc.CSLoader:createTimeline(path)
        self.mCurrentMark:getParent():runAction(self.mMarkAction)
        self.mMarkAction:gotoFrameAndPlay(0,self.mMarkAction:getDuration(),true)
    end

end

function Npc:stopMarkAnimation()
    if self.mMarkAction then
        self:stopAction(self.mMarkAction)
        self.mMarkAction = nil
    end
end

function Npc:removeMarkNode()
    if not cc.isDead(self.mCurrentMark) then
        self:stopMarkAnimation()
        self.mCurrentMark:removeFromParent()
        self:removeAttachment(self.mCurrentMark)
        self.mCurrentMark = nil
    end
end

function Npc:createMark(mark)
    if NpcMarkResPath[mark] then
        if self.mCurrentMark ~= nil then
            self:setCurrentMark(nil)
        end
--        print("================> createMark", mark, self:getId())
        local markNode = NodeFactory:createMark(NpcMarkResPath[mark], self)
        self:setCurrentMark(markNode)
        self:playMarkAnimation(NpcMarkResPath[mark])

--        self:currentMarkFadeIn()
        return true
    else
        return false
    end
end

--function Npc:doSetMarkVisible(visible,mark)
--    if mark == "guild" and UD:isInGuild() then
--        self:setCurrentMark(nil)
--        return
--    end
--    if self.mCurrentMark == nil and visible then
--        local result = self:createMark(mark)
--        if not result then
--            return
--        end
--    end
--    if visible then
--        self:currentMarkFadeIn()
--    else
--        self:currentMarkFadeOut()
--    end
--end

--function Npc:getBuildMarkNodePath()
--    local path
--    if self:getMark() then
--        path = NpcMarkResPath[self:getMark()]
--    end
--    return path
--end

local Npc_updateBubblePosition = Npc.updateBubblePosition
local Npc_updatePlayStyleAction = Npc.updatePlayStyleAction

function Npc:updateFrameLower(dt)
    Npc_updateBubblePosition(self)
end

function Npc:updateFrame(dt)
    Npc_updatePlayStyleAction(self,dt)

    local curPosXExact = NodeGetPositionXFunc(self)
    local curPosX = MathFloor(curPosXExact)
    local targetX = self.mTargetX or curPosX -- mTargetX 取值为整数

    if targetX ~= curPosX then
        if curPosX < targetX then
            self:setDirection(Role.DIR_RIGHT)
        else
            self:setDirection(Role.DIR_LEFT)
        end

        self:forceDirection()

        local disX = self:getSpeed() * dt

        local newX = curPosXExact + disX * self.mDirection

        if self.mDirection == Role.DIR_LEFT then
            if newX < targetX then
                newX = targetX
            end
        else
            if newX > targetX then
                newX = targetX
            end
        end

        self:autoMoveAction()

        self:noticeEventLostFocus()

        self:rayCastFoothold(newX)
    else
        if self:getEventMove() then
            self:setEventMove(false)
            self:stopEventAction()
            if self.mArriveCallback then
                self.mArriveCallback()
            end
        end
    end

    if self.mAdjustPos then
        self:adjustPos(false)
        self:forceDirection()
        self:rayCastFoothold(NodeGetPositionXFunc(self))
    end
end

function Npc:setBattleId(battleId)
    self.battleId = battleId
end

function Npc:getBattleId()
    return self.battleId
end

function Npc:setMineId(mineId)
    self.mineId = mineId
end

function Npc:getMineId()
    return self.mineId
end

return Npc
